package com.lee.article.service.impl;


import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.pagehelper.PageHelper;
import com.lee.api.client.BoardClient;
import com.lee.api.client.UserClient;
import com.lee.article.mapper.ArticleMapper;
import com.lee.article.service.IArticleService;
import com.lee.article.service.IArticleTopCacheService;
import com.lee.common.exception.ApplicationException;
import com.lee.common.model.*;
import com.lee.common.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lishuo
 * Date: 2023-09-30
 * Time: 21:06
 */
@Slf4j
@Service
public class ArticleServiceImpl implements IArticleService {
    // 注入DAO
    @Resource
    private ArticleMapper articleMapper;

    @Autowired
    private UserClient userService;

    @Autowired
    private BoardClient boardService;

    @Autowired
    private IArticleTopCacheService iArticleTopCacheService;

    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    @Override
    public void create(Article article) {
        // 1.参数校验
        if (article == null
        || article.getUserId() == null
        || article.getBoarId() == null
        || StringUtil.isEmpty(article.getTitle())
        || StringUtil.isEmpty(article.getContent())) {
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
        }
        // 2.设置默认数据
        article.setVisitCount(0); // 访问数
        article.setReplyCount(0); // 回复数
        article.setLikeCount(0);  // 点赞数
        article.setDeleteState((byte) 0);
        article.setState((byte) 0);
        Date date = new Date();
        article.setCreateTime(date);
        article.setUpdateTime(date);
        // 3.调用DAO插入数据库
        int articleRow = articleMapper.insertSelective(article);
        // 判断受影响的行数
        if (articleRow <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED+"受影响的行数不等于1.");
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
        }
        // 4.根据userId获取用户信息
        User user = userService.selectById(article.getUserId());
        // 判断用户信息是否有效
        if (user == null) {
            // 打印日志
            log.warn(ResultCode.ERROR_IS_NULL.toString()+"user id = "+article.getUserId());
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.ERROR_IS_NULL));
        }
        // 5.用户帖子数量+1
        userService.addOneArticleCountById(user.getId());
        // 6.根据boardId获取板块信息
        Board board =  boardService.selectById(article.getBoarId());
        // 判断板块信息是否有效
        if (board == null) {
            // 打印日志
            log.warn(ResultCode.ERROR_IS_NULL.toString()+"board id = "+article.getBoarId());
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.ERROR_IS_NULL));
        }
        // 7.板块帖子数量+1
        boardService.addOneArticleCountById(board.getId());
        // 8.打印成功日志
        log.info(ResultCode.SUCCESS.toString() + " user id = " + article.getUserId()
                + ", board id = " + article.getBoarId()+ ", article id = "+article.getId() + "发帖成功");

    }

    @Override
    public List<Article> selectAll() {
        // 1.调用DAO
        List<Article> articles = articleMapper.selectAll();
        // 2.将结果返回
        return articles;
    }

    @Override
    public List<Article> selectAllByBoardId(Long id) {
        // 1.参数校验
        if (id == null || id <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
        }
        // 2.调用DAO并检验板块是否存在
        Board board = boardService.selectById(id);
        if (board == null) {
            // 打印日志
            log.warn(ResultCode.ERROR_IS_NULL.toString()+"board id = "+id);
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.ERROR_IS_NULL));
        }

        // 新增逻辑1：对板块“热榜Top10” 单独处理
        if (id == 10) {
            // 从Redis中获取
            List<Article> hotArticleLists = iArticleTopCacheService.getHotArticleLists(10);
            return hotArticleLists;
        }

        // 3.调用DAO查询指定板块下的帖子列表
        List<Article> articles = articleMapper.selectAllByBoardId(id);
        // 4.返回结果
        return articles;
    }

    @Override
    public Article selectDetailById(Long id) {
        // 1.参数校验
        if (id == null || id <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
        }

        // 新增逻辑2：查询帖子前先查询redis
        Article hotArticleDetail = iArticleTopCacheService.getHotArticleDetail(id);
        if (hotArticleDetail != null) {
            // 这里要注意，需要手动更新一下浏览量，否则浏览量就就丢失了
            // 3.更新数据库中的文章访问量
            Article article = new Article();
            article.setId(hotArticleDetail.getId());
            article.setVisitCount(hotArticleDetail.getVisitCount()+1); // 访问量加1
            int row = articleMapper.updateByPrimaryKeySelective(article);
            // 判断是否更新成功
            if (row <= 0) {
                // 打印日志
                log.warn(ResultCode.FAILED+"受影响的行数不等于1.");
                // 抛出异常
                throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
            }

            // 新增逻辑4：浏览时更新map
            iArticleTopCacheService.articleAddVisitCount(article);

            // 返回结果
            hotArticleDetail.setVisitCount(article.getVisitCount());
            return hotArticleDetail;
            // 否则查询数据库
        }

        // 2.调用DAO并检验帖子是否存在
        Article article = articleMapper.selectDetailById(id);
        if (article == null) {
            // 打印日志
            log.warn(ResultCode.ERROR_IS_NULL.toString()+"article id = "+id);
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.ERROR_IS_NULL));
        }
        // 3.更新数据库中的文章访问量
        Article updateArticle = new Article();
        updateArticle.setId(article.getId());
        updateArticle.setVisitCount(article.getVisitCount()+1); // 访问量加1
        int row = articleMapper.updateByPrimaryKeySelective(updateArticle);
        // 判断是否更新成功
        if (row <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED+"受影响的行数不等于1.");
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
        }

        // 4.返回正确访问量的帖子详情
        article.setVisitCount(article.getVisitCount()+1);

        // 新增逻辑4：浏览时更新map
        iArticleTopCacheService.articleAddVisitCount(article);

        return article;
    }

    @Override
    public Article selectById(Long id) {
        // 1.参数校验
        if (id == null || id <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
        }
        // 2.调用DAO
        Article article = articleMapper.selectByPrimaryKey(id);
        // 3.返回结果
        return article;
    }

    @Override
    public void modify(Long id, String title, String content) {
        // 1.参数校验
        if (id == null || id <= 0 || StringUtil.isEmpty(title) || StringUtil.isEmpty(content)) {
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
        }
        // 2.构建更新帖子对象
        Article updateArticle = new Article();
        updateArticle.setId(id); // 设置 id
        updateArticle.setTitle(title); // 设置标题
        updateArticle.setContent(content); // 设置正文
        updateArticle.setUpdateTime(new Date()); // 设置更新日期
        // 3.调用DAO
        int row = articleMapper.updateByPrimaryKeySelective(updateArticle);
        // 判断是否更新成功
        if (row <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED+"受影响的行数不等于1.");
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
        }
    }

    @Override
    public void thumbsUpById(Long id) {
        // 1.参数校验
        if (id == null || id <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
        }
        // 2.调用DAO查询并校验帖子
        Article article = articleMapper.selectByPrimaryKey(id);
        // 帖子不存在
        if (article == null || article.getDeleteState() == 1) {
            // 打印日志
            log.warn("帖子不存在 article id = "+id);
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.ERROR_IS_NULL));
        }
        // 帖子状态异常
        if (article.getState() == 1) {
            // 打印日志
            log.warn(ResultCode.FAILED_ARTICLE_STATUS+"article id = "+id);
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_ARTICLE_STATUS));
        }
        // 3.构造点赞更新对象
        Article updateArticle = new Article();
        updateArticle.setId(id);
        updateArticle.setLikeCount(article.getLikeCount()+1);
        // 4.调用DAO更新点赞数量
        int row = articleMapper.updateByPrimaryKeySelective(updateArticle);
        // 判断是否更新成功
        if (row <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED+"受影响的行数不等于1.");
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
        }

        // 新增逻辑5：点赞时时更新map
        article.setLikeCount(updateArticle.getLikeCount());
        iArticleTopCacheService.articleAddLikeCount(article);
    }

    @Override
    public void deleteById(Long id) {
        // 1.参数校验
        if (id == null || id <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
        }
        // 2.调用DAO查询并校验帖子
        Article article = articleMapper.selectByPrimaryKey(id);
        // 帖子不存在
        if (article == null || article.getDeleteState() == 1) {
            // 打印日志
            log.warn("帖子不存在 article id = "+id);
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.ERROR_IS_NULL));
        }
        // 帖子已封贴
        if (article.getState() == 1) {
            // 打印日志
            log.warn(ResultCode.FAILED_ARTICLE_STATUS+"article id = "+id);
            // 返回错误信息
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_ARTICLE_STATUS));
        }
        // 3.构造更新对象
        Article updateArticle = new Article();
        updateArticle.setId(article.getId());
        updateArticle.setDeleteState((byte) 1);
        // 4.调用DAO更新
        int row = articleMapper.updateByPrimaryKeySelective(updateArticle);
        // 判断是否更新成功
        if (row <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED+"受影响的行数不等于1.");
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
        }
        // 5.更新板块中帖子数量
        boardService.subOneArticleCountById(article.getBoarId());
        // 6.更新用户发帖数量
        userService.subOneArticleCountById(article.getUserId());
        // 7.打印日志
        log.info("帖子删除成功：article id = "+id);

        // 新增逻辑7：删除时更新map
        iArticleTopCacheService.articleDelete(article);
    }

    @Override
    public void addOneArticleReplyCountById(Long id) {
        // 1.参数校验
        if (id == null || id <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
        }
        // 2.调用DAO查询并校验帖子
//        Article article = articleMapper.selectByPrimaryKey(id);
        Article article = articleMapper.selectDetailById(id);

        // 帖子不存在
        if (article == null || article.getDeleteState() == 1) {
            // 打印日志
            log.warn("帖子不存在 article id = "+id);
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.ERROR_IS_NULL));
        }
        // 帖子已封贴
        if (article.getState() == 1) {
            // 打印日志
            log.warn(ResultCode.FAILED_ARTICLE_STATUS+"article id = "+id);
            // 返回错误信息
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_ARTICLE_STATUS));
        }
        // 3.构造更新对象
        Article updateArticle = new Article();
        updateArticle.setId(id);
        updateArticle.setReplyCount(article.getReplyCount() + 1);
        // 4.调用DAO更新回复数
        int row = articleMapper.updateByPrimaryKeySelective(updateArticle);
        // 判断是否更新成功
        if (row <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED+"受影响的行数不等于1.");
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
        }

        // 新增逻辑6：评论时更新map
        article.setReplyCount(updateArticle.getReplyCount());
        if (article.getUser() == null) {
            article.setUser(new User());
        }
        if (article.getBoard() == null) {
            article.setBoard(new Board());
        }
        iArticleTopCacheService.articleAddReplyCount(article);
    }

    @Override
    public List<Article> selectByUserId(Long userId) {
        // 1.校验参数
        if (userId == null || userId <= 0) {
            // 打印日志
            log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
        }
        // 2.调用DAO查询用户是否存在
        User user = userService.selectById(userId);
        if (user == null) {
            // 打印日志
            log.warn(ResultCode.FAILED_USER_NOT_EXISTS.toString()+"user id = " + userId);
            // 抛出异常
            throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_NOT_EXISTS));
        }
        // 3.调用DAO获取该用户的帖子列表
        List<Article> result = articleMapper.selectByUserId(userId);
        // 4.返回结果
        return result;
    }

    @Override
    public List<Article> selectTopnArticles(Integer n) {
        return articleMapper.selectTopnArticles(n);
    }

    @Override
    public PageResult listByPage(Integer pIndex, Integer pSize,Long boardId) {
        Page<Article> page = null;
        List<Article> articles = null;
        // 1. 执行查询
        if (boardId == null) {
            // 分页查询首页文章列表
            // 0.设置分页参数（只对紧跟startPage后的Mybatis方法进行分页）
            PageHelper.startPage(pIndex,pSize);
            articles = articleMapper.selectAll();
            page = (Page<Article>) articles;
        } else {
            // 分页查询板块下列表
            if (boardId < 0) {
                throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
            }
            // 2.1 获取板块信息
            Board board = boardService.selectById(boardId);
            if (board == null) {
                throw new ApplicationException(AppResult.failed(ResultCode.ERROR_IS_NULL));
            }
            // 新增逻辑1：对板块“热榜Top10” 单独处理
            if (boardId == 10) {
                // 从Redis中获取
                List<Article> hotArticleLists = iArticleTopCacheService.getHotArticleLists(10);
                // 3. 封装返回结果
                PageResult pageResult = new PageResult();
                // 设置结果集
                pageResult.setArticles(hotArticleLists);
                // 设置总数
                pageResult.setTotal(hotArticleLists.size());
                // 4. 返回结果
                return pageResult;
            }
            // 2.2 查询
            // 0.设置分页参数（只对紧跟startPage后的Mybatis方法进行分页）
            PageHelper.startPage(pIndex,pSize);
            articles = articleMapper.selectAllByBoardId(boardId);
            page = (Page<Article>) articles;
        }

        // 3. 封装返回结果
        PageResult pageResult = new PageResult();
        // 设置结果集
        pageResult.setArticles(page.getRecords());
        // 设置总数
        pageResult.setTotal(page.getTotal());
        // 4. 返回结果
        return pageResult;
    }
}
